#pragma once
#ifndef __TSGGL__
#define __TSGGL__


#include "tsgCore.h"
#include <list>


namespace tsg
{
	/** OpenGL-based scenegraph nodes.  You should not add anything to this namespace. */
	namespace gl
	{

		using std::list;


		/** Open a window and display a scenegraph.  @param root Root node of the scenegraph.  It should usually be a Separator node. @param width Window width. Must be greater than zero. @param height Window height.  Must be greater than zero. */
		void  display( Node* root, int width, int height );


		/** Group of nodes.  This enables creation of a scenegraph by providing the concept of child nodes. */
		class Group : public Node
		{
		public:
			Parameter<bool>  active; /**< Determines whether children should be processed. Animate this parameter when you want parts of the graph to sometimes be ignored. */
		public:
			Group();
			~Group();
		public:
			/** Add a child node. @return The pointer passed in. */
			Node*  addChild    ( Node* );
			/** Remove all occurences of a given node. This only removes from the children; it does not traverse further down the graph. */
			void removeChild ( Node* );
		protected:
			virtual void  doRender();
		protected:
			list<Node*>  _children;
		};


		/** Group of nodes where transformation and attribute changes are isolated. @see Group */
		class Separator : public Group
		{
		public:
			Separator( bool separate_attribs =true );
		protected:
			void  doRender();
		private:
			bool  _attrib;
		};


		/** The virtual camera. Each graph should include only one of these nodes and it should be one of the first children of the root node. */
		class Camera : public Node
		{
		public:
			Parameter<float>  aspectRatio; /**< View volume aspect ratio.  In most cases this should match the aspect ratio of the window it will be displayed in. */
			Parameter<float>  farDistance; /**< Far clipping plane distance.  This must be positive and greater than nearDistance. */
			Parameter<float>  fieldOfView; /**< Vertical field of view angle.  Angle is expressed in degrees. This should be positive and less than 180; usually around 45. */
			Parameter<float>  nearDistance;/**< Near clipping plane distance.  This must be positive and less than farDistance. */
		public:
			/** Default constructor. */
			Camera( );
			/** Value constructor. */
			Camera( float aspect, float fov, float nearDist, float farDist );
		private:
			void  doRender();
		};


		/** Positions the camera. Each graph will usually require one of these.  In most cases this should be the first transformation processed during traversal. */
		class LookAt : public Node
		{
		public:
			Parameter<Vector3>  at; /**< 3D point for the camera to look at. */
			Parameter<Vector3>  eye;/**< 3D position of the camera. */
			Parameter<Vector3>  up; /**< Approximate indication of the camera's up direction.  Often this will match the world's up direction. */
		public:
			/** Default constructor. */
			LookAt( );
			/** Value constructor. */
			LookAt( const Vector3& eye, const Vector3& at, const Vector3& up );
		private:
			void  doRender();
		};


		/** Translate transformation. */
		class Translate : public Node
		{
		public:
			Parameter<Vector3>  xyz; /**< Amount to translate by. */
		public:
			/** Default constructor. */
			Translate();
			/** Value constructor. */
			Translate( const Vector3& );
			/** Value constructor. */
			Translate( float, float, float );
		private:
			void  doRender();
		};


		/** Rotate transformation. */
		class Rotate : public Node
		{
		public:
			Parameter<float>    angle; /**< Angle to rotate by.  Angle is expressed in degrees. */
			Parameter<Vector3>  axis;  /**< An axis to rotate around.  This doesn't have to be a unit vector but it mustn't be (0,0,0). */
		public:
			/** Default constructor. */
			Rotate( );
			/** Value constructor. */
			Rotate( float angle, const Vector3& axis );
			/** Value constructor. */
			Rotate( float angle, float ax, float ay, float az );
		private:
			void  doRender();
		};


		/** Scale transformation. */
		class Scale : public Node
		{
		public:
			Parameter<Vector3>  xyz; /**< Amount to scale by. Each component should usually be non-zero. */
		public:
			/** Default constructor. */
			Scale( );
			/** Value constructor. */
			Scale( const Vector3& );
			/** Value constructor. */
			Scale( float, float, float );
		private:
			void  doRender();
		};


		/** Generic transformation. Use this to create your own transformations that don't have nodes of their own. */
		class Transformation : public Node
		{
		public:
			Parameter<Matrix>  matrix; /**< Matrix to represent the transformation. This should be a well behaved matrix (i.e. be invertable). */
		public:
			/** Default constructor. */
			Transformation( );
			/** Value constructor. */
			Transformation( const Matrix& );
		private:
			void  doRender();
		};


		/** Drawing material. */
		class Material : public Node
		{
		public:
			Parameter<Colour>  ambient;  /**< Colour to be reflected by ambient light.  This often matches the diffuse colour. */
			Parameter<Colour>  diffuse;  /**< Colour to be reflected by diffuse light.  This is the basic colour of the material. */
			Parameter<Colour>  specular; /**< Colour to be reflected by specular light. This is the colour of shiny highlights.  It is usually set to white except for metals when it matches the diffuse colour. */
			Parameter<float>   shininess;/**< How shiny the material is. This must be greater than 1 and usually in the range [1,128].  64 is an appropriate default value. */
		public:
			/** Default constructor. */
			Material();
			/** Value constructor. */
			Material( const Colour& ambient, const Colour& diffuse, const Colour& specular, float shininess );
		private:
			void  doRender();
		};


		/** Creates a cube. */
		class Cube : public Node
		{
		public:
			Parameter<float>  size; /**< Width, height, and depth of the cube. Should be positive. */
		public:
			Cube( );
			Cube( float size );
		private:
			void  doRender();
		private:
			static int  _list;
		};


		/** Create a composite cubic Bezier curve. */
		class Bezier3Curve : public Node
		{
		public:
			Parameter<int>    complexity;    /**< Number of subdivisions per curve segment. */
			vector<Vector3>   controlPoints; /**< A series of control points; four at a time make a curve. */
		public:
			Bezier3Curve( );
			~Bezier3Curve( );
		private:
			void  doRender();
		private:
			int  _list;
		};


		/** Create a composite Nurbs curve. */
		class NurbsCurve : public Node
		{
		public:
			Parameter<int>    complexity;    /**< Number of subdivisions per curve segment. */
			vector<Vector4>   controlPoints; /**< A series of homogeneous control points. */
			vector<float>     knotVector;    /**< Parameter values for the basis functions. There must be nPoints+degree+1 knot values. */
		public:
			NurbsCurve( int degree );
			~NurbsCurve( );
		private:
			void  doRender();
		private:
			int  _degree;
			int  _list;
		};


		/** Create a mesh from a TSF scene file. */
		class TSFMesh : public Node
		{
		public:
			/** Constructor. The first mesh found in the file will be loaded. @param filename Must be the name of a TSF formatted XML file in the working directory. */
			TSFMesh ( const char* filename );
		private:
			void  doRender();
		private:
			vector<float>  _data;
			int            _list;
		};


		class IndexedMesh : public Node
		{
		public:
			IndexedMesh ( );
			IndexedMesh ( const char* collada_filename );
			~IndexedMesh( );
		public:
			vector<int>              indexArray;
			ArrayParameter<Vector3>  vertexArray;
			ArrayParameter<Vector3>  normalArray;
			ArrayParameter<Vector2>  texcoordArray;
		private:
			void  onChange();
			void  doRender();
		private:
			int  _list;
		};

	};
};


#endif
